/*
Copyright (c) 2010, Andri Yngvason
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of ArDAS nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <util/delay.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "ds18x20.h"
#include "../owire.h"
#include "../cmd.h"


static void *ds18x20_cmd_list[] = {
	"init", &ds18x20_cmd_init,
	"sample", &ds18x20_cmd_sample,
	"read", &ds18x20_cmd_read,
	NULL
};


int ds18x20_init(int pin) {
	owire_init(pin);

	if(!owire_reset(pin)) {
		return 0;
	}

	owire_tx(pin, 0x33); // read rom

	return owire_rx(pin); //return family code
}


int ds18x20_sample(int pin) {
	if(!owire_reset(pin)) {
		return 0;
	}

	owire_tx(pin, 0xcc); // skip rom
	owire_tx(pin, 0x44); // convert temp
	
	return 1;
}


int16_t ds18x20_read(int pin) {
	uint8_t msb, lsb, cr, f;
	int16_t r = 0;

	owire_reset(pin);
	owire_tx(pin, 0x33); // read rom
	f = owire_rx(pin);

	owire_reset(pin);
	owire_tx(pin, 0xcc); // skip rom
	owire_tx(pin, 0xbe); // read scratchpad

	lsb = owire_rx(pin);
	msb = owire_rx(pin);
	
	if(f == 0x8 || f == 0x10) {
		/* DS1820 or DS18S20 */
		owire_rx(pin);
		owire_rx(pin);
		owire_rx(pin);
		owire_rx(pin);
		cr = owire_rx(pin);

		r = ((lsb & 0xfe)<<3) + 12 - cr;
		r *= msb ? -1 : 1;
	} else {
		/* DS18B20 */
		r = ((msb & 0x7) << 8) | lsb;
		r *= (msb & 0x80) ? -1 : 1;
	}

	return r;
}


int ds18x20_cmd(char *line) {
	int (*f)(char*);
	f = cmd_find(&line,ds18x20_cmd_list,&strcmp);

	if(f == cmd_notfound) {
		printf("ERROR: Sensor command not found.\n");
		return 1;
	}

	if(f == NULL) {
		return 0;
	}

	return f(line);
}


int ds18x20_cmd_init(char *line) {
	int f;
	char *p;

	p = strsep(&line, " \n\r");
	if(p == NULL) {
		printf("ERROR: sensor DS18X20 init: please specify a pin number.\n");
		return 1;
	}

	f = ds18x20_init(atoi(p));
	switch(f) {
		case 0:
			printf("ERROR: no onewire device is connected to pin %s.\n", p);
			return 1;
		case 0x8:
			printf("sensor DS1820 detected on pin %s.\n", p);
			return 0;
		case 0x10:
			printf("sensor DS18S20 detected on pin %s.\n", p);
			return 0;
		case 0x28:
			printf("sensor DS18B20 detected on pin %s.\n", p);
			return 0;
		default:
			printf("ERROR: device on pin %s is not a DS18X20 sensor.\n", p);
			return 1;
	}

	return 1;
}


int ds18x20_cmd_sample(char *line) {
	char *p;

	p = strsep(&line, " \n\r");
	if(p == NULL) {
		printf("ERROR: sensor DS18X20 sample: please specify a pin number.\n");
		return 1;
	}

	if(!ds18x20_sample(atoi(p))) {
		printf("ERROR: no onewire device present on pin %s.\n", p);
		return 1;
	}

	return 0;
}


int ds18x20_cmd_read(char *line) {
	char *p;

	p = strsep(&line, " \n\r");
	if(p == NULL) {
		printf("ERROR: sensor DS18X20 read: please specify a pin number.\n");
		return 1;
	}

	printf("DS18X20[%s]: %d\n", p, ds18x20_read(atoi(p)));

	return 0;
}

